<template>
  <div class="validate-input-container pb-3">
    <input
      v-if="tag !== 'textarea'"
      type="text"
      class="form-control"
      :class="{ 'is-invalid': inputRef.error }"
      v-model="inputRef.val"
      @blur="ValidateInput"
      @input="updateValue"
      v-bind="$attrs"
    />
    <textarea
      v-else
      class="form-control"
      :class="{ 'is-invalid': inputRef.error }"
      @blur="ValidateInput"
      @input="updateValue"
      :value="inputRef.val"
      v-bind="$attrs"
    ></textarea>
    <span class="invalid-feedback" v-if="inputRef.error">{{
      inputRef.message
    }}</span>
  </div>
</template>

<script lang="ts">
import {
  defineComponent,
  reactive,
  PropType,
  onMounted,
  watch,
  computed
} from 'vue'
import { emitter } from './ValidateForm.vue'
const emailReg =
  /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
interface RuleProp {
  type: 'required' | 'email' | 'custom' // 他可以同时实现两种，必选的 | 必填的
  message: string
  validator?: () => boolean
}
// RulesProp是在RuleProp基础上扩展的
export type RulesProp = RuleProp[]
// 将添加搭配父元素身上的属性放到子元素上，需要两件事
// 1.把父元素的东西删除 inheritAttrs:false
// 2.使用v-bind="$attrs"绑定上元素
export type TagType = 'input' | 'textarea'
export default defineComponent({
  props: {
    // 我们在上边已经定义了他是一个数组
    rules: Array as PropType<RulesProp>,
    // loginIndex中的v-model一样
    modelValue: String,
    tag: {
      type: String as PropType<TagType>
    }
  },
  setup (props, context) {
    const inputRef = reactive({
      // 如果他一开始有值的话，应该在你的modelValue取值
      // 如果刷新页面你的Input里有值是不是要在页面显示出来，显示出来就是props.modelVal
      // val: props.modelValue || '', // 内容 他应该有这两种情况
      val: computed({
        get: () => props.modelValue || '',
        set: (val) => {
          // 发送事件不用打印两次
          context.emit('updata:mpdelValue', val)
        }
      }),
      error: false, // 返回一个错误，就是他的一个错误信息
      message: '' // 返回的错误信息
    })

    // 你往里面输入内容鼠标只起到了一个失焦和获焦的作用
    // 你输入内容应该是你的键盘事件
    const updateValue = (e: KeyboardEvent) => {
      // HTMLInputElement 是input 标签的这个元素
      // 就是拿到你的数据源推断他是不是当前元素，然后收到Value值赋给targetvallue
      // targetValue input里面的value
      const targetValue = (e.target as HTMLInputElement).value
      inputRef.val = targetValue || ''
      // 把事件抛出去传出去
      context.emit('value', targetValue)
    }
    watch(
      () => props.modelValue,
      (newValue) => {
        inputRef.val = newValue || ''
      }
    )
    // 失去焦点出发
    const ValidateInput = () => {
      if (props.rules) {
        // 遍历每一条规则
        const allPassed = props.rules.every((rule) => {
          // 检测数组中的元素是否通过检测
          let passed = true
          inputRef.message = rule.message
          switch (rule.type) {
            case 'required':
              // 不能为空才可以通过
              passed = inputRef.val.trim() !== ''
              break
            case 'email':
              // 检测你输入框里的内容
              passed = emailReg.test(inputRef.val)
              break
            case 'custom':
              passed = rule.validator ? rule.validator() : true
              break
            default: // 如果都不符合
              break
          }
          // 把最后结果返回出去
          return passed
        })
        // 如果全是true 取反为false不让消息提示出来
        inputRef.error = !allPassed
        return allPassed
      }
    }
    onMounted(() => {
      // 传到form表单
      emitter.emit('form-item-created', ValidateInput)
    })
    return {
      inputRef,
      updateValue,
      ValidateInput
    }
  }
})
</script>

<style scoped></style>
